/* Copyright (c) 2021, National University of Defense Technology. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "coo_matrix.h"
#include "coo_matvec.h"

namespace YHAMG
{

COO_Matrix::COO_Matrix()
	: ref(0),
	size{0, 0},
	nnz(0),
	rowind(0),
	colind(0),
	values(0)
{
}

COO_Matrix::COO_Matrix(int _n, int _m, int _nnz, int* _rowind, int* _colind, double* _values, int _ref)
	: ref(_ref),
	size{_n, _m},
	nnz(_nnz),
	rowind(_rowind),
	colind(_colind),
	values(_values)
{
}

COO_Matrix::COO_Matrix(const COO_Matrix& A)
	: ref(0),
	size{A.size[0], A.size[1]},
	nnz(A.nnz), 
	rowind(new int[A.nnz]),
	colind(new int[A.nnz]),
	values(new double[A.nnz])
{
	int* Ai = A.rowind;
	int* Aj = A.colind;
	double* Av = A.values;

	for (int j = 0; j < nnz; ++j)
	{
		rowind[j] = Ai[j];
		colind[j] = Aj[j];
		values[j] = Av[j];
	}
}

COO_Matrix::COO_Matrix(COO_Matrix&& A)
	: ref(A.ref),
	size{A.size[0], A.size[1]},
	nnz(A.nnz), 
	rowind(A.rowind),
	colind(A.colind),
	values(A.values)
{
	A.ref = 1;
}

COO_Matrix::~COO_Matrix()
{
	if (!ref)
	{
		if (rowind) delete[] rowind;
		if (colind) delete[] colind;
		if (values) delete[] values;
	}
}

COO_Matrix& COO_Matrix::operator=(const COO_Matrix& A)
{
	Free();

	int* Ai = A.rowind;
	int* Aj = A.colind;
	double* Av = A.values;

	size[0] = A.size[0];
	size[1] = A.size[1];
	nnz = A.nnz;

	rowind = new int[nnz];
	colind = new int[nnz];
	values = new double[nnz];

	for (int j = 0; j < nnz; ++j)
	{
		rowind[j] = Ai[j];
		colind[j] = Aj[j];
		values[j] = Av[j];
	}

	return *this;
}

COO_Matrix& COO_Matrix::operator=(COO_Matrix&& A)
{
	Free();

	ref = A.ref;
	size[0] = A.size[0];
	size[1] = A.size[1];
	nnz = A.nnz;
	rowind = A.rowind;
	colind = A.colind;
	values = A.values;

	A.ref = 1;

	return *this;
}


void COO_Matrix::Free()
{
	if (!ref)
	{
		if (rowind) delete[] rowind;
		if (colind) delete[] colind;
		if (values) delete[] values;
	}
	size[0] = 0;
	size[1] = 0;
	nnz = 0;
	rowind = 0;
	colind = 0;
	values = 0;
	ref = 0;
}

void COO_Matrix::Reference(const COO_Matrix& A)
{
	if (!ref)
	{
		if (rowind) delete[] rowind;
		if (colind) delete[] colind;
		if (values) delete[] values;
	}
	size[0] = A.size[0];
	size[1] = A.size[1];
	nnz = A.nnz;
	rowind = A.rowind;
	colind = A.colind;
	values = A.values;
	ref = 1;
}

int COO_Matrix::InSize() const
{
	return size[1];
}

int COO_Matrix::OutSize() const
{
	return size[0];
}

void COO_Matrix::Apply(const Vector& x, const Vector& y) const
{
	COO_MatVec(*this, x, y);
}

}
